/*->w.ckcpro */

/* C K C P R O  -- C-Kermit Protocol Module, in Wart preprocessor notation. */
/*
 Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
 Columbia University Center for Computing Activities.
 First released January 1985.
 Copyright (C) 1985, 1990, Trustees of Columbia University in the City of New 
 York.  Permission is granted to any individual or institution to use, copy, or
 redistribute this software so long as it is not sold for profit, provided this
 copyright notice is retained. 
*/



#include <stdlib.h>
#include <stdio.h>


#include "h.def"


#include "ckcdeb.h"
#include "ckcker.h"

#include "ckcdef.h"


/*
 Note -- This file may also be preprocessed by the Unix Lex program, but 
 you must indent the above #include statements before using Lex, and then
 restore them to the left margin in the resulting C program before compilation.
 Also, the invocation of the "wart()" function below must be replaced by an
 invocation  of the "yylex()" function.  It might also be necessary to remove
 comments in the %%...%% section.
*/

/* State definitions for Wart (or Lex) */
%states ipkt rfile rattr rdata ssinit ssfile ssattr ssdata sseof sseot
%states serve generic get rgen


  int what = W_NOTHING;

/* Local variables */
  static char vstate = 0;               /* Saved State   */
  static char vcmd = 0;                 /* Saved Command */

  int x;                                /* General-purpose integer */
  char *s;                              /* General-purpose string pointer */


/* Macros - Note, BEGIN is predefined by Wart (and Lex) as "state = ", */
/* BEGIN is NOT a GOTO! */

#define TINIT  if (tinit() < 0) return(-9)
#define SERVE  TINIT; nakstate = 1; what = W_NOTHING; BEGIN serve
#define RESUME if (server) { SERVE; } else { sleep(2); return(0); }
#define QUIT x=quiet; quiet=1; clsif(1); clsof(1); /* tsecs=gtimer(); */ quiet=x; \
 return(1)


%%
/* Protocol entry points, one for each start state (sstate) */
/* nakstate = 1 means we're in a receiving state, in which we can send NAKs */

s { TINIT;                              /* Do Send command */
    if (sinit()) BEGIN ssinit;
       else RESUME; }

v { TINIT; nakstate = 1; BEGIN get; }                           /* Receive */
r { TINIT; vstate = get;  vcmd = 0;   sipkt('I'); BEGIN ipkt; } /* Get */
c { TINIT; vstate = rgen; vcmd = 'C'; sipkt('I'); BEGIN ipkt; } /* Host */
g { TINIT; vstate = rgen; vcmd = 'G'; sipkt('I'); BEGIN ipkt; } /* Generic */

x { sleep(1); SERVE; }                  /* Be a Server */

a { errpkt("KW0"); /* User cancelled transaction */
                   /* "ABEND" -- Tell other side. */
    success = 0;          
    return(0); }                        /* Return from protocol. */

/* Dynamic states: <current-states>input-character { action } */

<rgen,get,serve>S {                     /* Receive Send-Init packet. */
    nakstate = 1;                       /* Can send NAKs from here. */
    rinit(rdatap);                      /* Set parameters */
    bctu = bctr;                        /* Switch to agreed-upon block check */
    zsetmode(bctu);
    resetc();                           /* Reset counters */
    what = W_RECV;                      /* Remember we're receiving */
/*    rtimer();    */                       /* Reset timer */
    BEGIN rfile;                        /* Go into receive-file state */
}

/* States in which we get replies back from commands sent to a server. */
/* Complicated because direction of protocol changes, packet number    */
/* stays at zero through I-G-S sequence, and complicated even more by  */
/* sliding windows buffer allocation. */

<ipkt>Y {                               /* Get ack for I-packet */
    spar(rdatap);                       /* Set parameters */
    getsbuf(winlo = 0);                 /* Set window-low back to zero */
    if (vcmd) {                         /* If sending a generic command */
        scmd(vcmd,cmarg);               /* Do that */
        vcmd = 0;                       /* and then un-remember it. */
    } else if (vstate == get) srinit(); /* If sending GET command, do that */
    winlo = 0;                          /* Again! */
    nakstate = 1;                       /* Can send NAKs from here. */
    BEGIN vstate;                       /* Switch to desired state */
}

<ipkt>E {                               /* Ignore Error reply to I packet */
    if (vcmd) {                         /* in case other Kermit doesn't */
        scmd(vcmd,cmarg);               /* understand I-packets. */
        vcmd = 0;                       /* Otherwise act as above... */
    }
    if (vstate == get) srinit();
    BEGIN vstate;
}

<get>Y {                /* Resend of previous I-pkt ACK, same seq number! */
    srinit();
}

/* States in which we're being a server */

<serve>I {                              /* Get I-packet */
    spar(rdatap);                       /* Set parameters from it */
    ack1(rpar());                       /* Respond with our own parameters */
    pktinit();                          /* Reinitialize packet numbers */
}

<serve>R {                              /* Get Receive-Init */
    srvptr = srvcmd;                    /* Point to server command buffer */
    decode(rdatap,putsrv);              /* Decode the GET command into it */
    cmarg = srvcmd;
    nfils = -1;                         /* Initialize number of files */
    nakstate = 0;                       /* Now I'm the sender! */
    if (sinit())                        /* Send Send-Init */
      BEGIN ssinit;                     /* If successful, switch state */
    else { SERVE; }                     /* Else back to server command wait */
}

<serve>G {                              /* Generic server command */
    srvptr = srvcmd;                    /* Point to command buffer */
    decode(rdatap,putsrv);              /* Decode packet data into it */
    putsrv('\0');                       /* Insert a couple nulls */
    putsrv('\0');                       /* for termination */
    if (srvcmd[0]) {
        sstate = srvcmd[0];                     /* Set requested start state */
        nakstate = 0;                   /* Now I'm the sender. */
        what = W_REMO;                  /* Doing a REMOTE command. */
        BEGIN generic;                  /* Switch to generic command state */
    } else {
        errpkt("KW1");/*Badly formed server command*/ /* report error */
        SERVE;                          /* & go back to server command wait */
    }
}

<serve>C {                              /* Receive Host command */
    srvptr = srvcmd;                    /* Point to command buffer */
    decode(rdatap,putsrv);              /* Decode command packet into it */
    putsrv('\0');                       /* Null-terminate */
    nakstate = 0;                       /* Now sending, not receiving */
    if (syscmd(srvcmd,""))              /* Try to execute the command */
      BEGIN ssinit;                     /* If OK, send back its output */
    else {                              /* Otherwise */
        errpkt("KW2");/*Can't do system command*/ /* report error */
        SERVE;                          /* & go back to server command wait */
    }
}

<serve>q { success = 0; QUIT; }         /* User typed Ctrl-C... */

<serve>. {                              /* Any other command in this state */
    errpkt("KW3");/*Unimplemented server function*/ /* we don't know about */
    SERVE;                              /* back to server command wait */
}

<generic>C {                            /* Got REMOTE CWD command */
        if (!cwd(srvcmd+1)) errpkt("KW4");/*Can't change directory*//*Try it*/
        SERVE;                          /* Back to server command wait */
}

<generic>D {                            /* REMOTE DIRECTORY command */
        if (syscmd(DIRCMD,srvcmd+2))    /* If it can be done */
          BEGIN ssinit;                 /* send the results back */
        else {                          /* otherwise */
            errpkt("KW5");/*Can't list directory*/ /* report failure */
            SERVE;                      /* & return to server command wait */
        }
}

<generic>E {                            /* REMOTE DELETE (Erase) command */
        if (syscmd(DELCMD,srvcmd+2))    /* Try to do it */
          BEGIN ssinit;                 /* If OK send results back */
        else {                          /* otherwise */
            errpkt("KW6");/*Can't remove file*/ /* report failure */
            SERVE;                      /* & return to server command wait */
        }
}

<generic>F {                            /* FINISH */
        ack();                          /* Acknowledge */
        return(0);                      /* Done */
}

<generic>L {                            /* BYE (LOGOUT) */
        ack();                          /* Acknowledge */
        return(zkself());               /* Try to log self out */
}

<generic>H {                            /* REMOTE HELP */
    if (sndhlp()) BEGIN ssinit;         /* Try to send it */
    else {                              /* If not ok, */
        errpkt("KW7"); /*Can't send help*/  /* send error message instead */
        SERVE;                          /* and return to server command wait */
    }
}



/* KW8 == REMOTE SET disabled*/
/* KW9 == Unknown REMOTE SET parameter*/ 

/*  remote set

<generic>S {                           
    if (!en_set) {
        errpkt("KW8");
        SERVE;
    } else {
        if (remset(srvcmd+1))          
          ack();                       
        else                           
          errpkt("KW9");
        SERVE;                       
    }
}

*/


<generic>T {                            /* REMOTE TYPE */
        if (syscmd(TYPCMD,srvcmd+2))    /* Try */
          BEGIN ssinit;                 /* OK */
        else {                          /* not OK */
            errpkt("KW10");/*Can't type file*/  /* give error message */
            SERVE;                      /* wait for next server command */
        }
}

<generic>U {                            /* REMOTE SPACE */
        x = *(srvcmd+1);                /* Get area to check */
        x = ((x == '\0') || (x == SP));
        x = (x ? syscmd(SPACMD,"") : syscmd(SPACM2,srvcmd+2));
        if (x)                          /* If we got the info */
          BEGIN ssinit;                 /* send it */
        else {                          /* otherwise */
            errpkt("KW11");/* Can't check space */  /* send error message */
            SERVE;                      /* and await next server command */
        }
}



/* KW12 == REMOTE WHO disabled */ 
/* KW13 == Can't do who command */

/*  WHO command 

<generic>W {                     
    if (!en_who) {
        errpkt("KW12");
        SERVE;
    } else {
        if (syscmd(WHOCMD,srvcmd+2)) 
          BEGIN ssinit;
        else {
            errpkt("KW13");
            SERVE;
        }
    }
}

*/



<generic>q { success = 0; QUIT; }       /* Ctrl-C typed */

<generic>. {                            /* Anything else in this state... */
    errpkt("KW14");/*Unimplemented REMOTE command*/ /* Complain */
    SERVE;                              /* and return to server command wait */
}

<rgen>Y {                               /* Short-Form reply */
    decode(rdatap,puttrm);              /* in ACK Data field */
    RESUME;
}

<rgen,rfile>F {                         /* File header */
    xflg = 0;                           /* Not screen data */
    if (!rcvfil(filnam)) {              /* Figure out local filename */
        errpkt("KW15");/*Can't transform filename*/ /* Trouble */
        RESUME;
    } else {                            /* OK to receive */
        encstr(filnam);                 /* Encode the name */
        ack1(encbuf);                   /* Send it back in ACK */
        initattr(&iattr);               /* Clear file attribute structure */
        window(wslotsn);                /* allocate negotiated window slots */
        BEGIN rattr;                    /* Now expect Attribute packets */
    }
}

<rgen,rfile>X {                         /* X-packet instead of file header */
    xflg = 1;                           /* Screen data */
    ack();                              /* Acknowledge the X-packet */
    initattr(&iattr);                   /* Initialize attribute structure */
    window(wslotsn);                    /* allocate negotiated window slots */
    BEGIN rattr;                        /* Expect Attribute packets */
}

<rattr>A {                              /* Attribute packet */
    if (discard)                        /* If SET COLLSION DISCARD  */
      ack1("N");                        /* refuse it */
    else if (gattr(rdatap,&iattr) == 0) /* Read into attribute structure */
      ack();                            /* If OK, acknowledge */
    else
      ack1(iattr.reply.val);            /* refuse to accept the file */
}

<rattr>D {                              /* First data packet */
    if (discard)        {               /* if we're discarding the file */
        ack1("X");                      /* just ack the data like this. */
        BEGIN rdata;                    /* and wait for more data packets. */
    } else {                            /* Not discarding. */
        if (xflg)                       /* If screen data */
          x = opent(&iattr);            /* "open" the screen */
        else                            /* otherwise */
          x = opena(filnam,&iattr);     /* open the file, with attributes */
        if (x) {                        /* If file was opened ok */
            if (decode(rdatap,putfil) < 0) { /* decode first data packet */
                errpkt("KW16"); /*Error writing data */
                RESUME;
            }
            ack();                      /* acknowledge it */
            BEGIN rdata;                /* and switch to receive-data state */
        } else {                        /* otherwise */
            errpkt("KW17");/*Can't open file*/  /* send error message */
            RESUME;                     /* and quit. */
        }
    }
}

<rfile>B {                              /* EOT, no more files */
    ack();                              /* Acknowledge */
  /*  tsecs = gtimer(); */                  /* Get timing for statistics */
    reot();                             /* Do EOT things */
    RESUME;                             /* and quit */
}

<rdata>D {                              /* Data packet */
    if (cxseen || discard)              /* If file interrupt */
      ack1("X");                        /* put "X" in ACK */
    else if (czseen)                    /* If file-group interrupt */
      ack1("Z");                        /* put "Z" in ACK */
    else if (decode(rdatap,putfil) < 0) { /* Normal case, */
        errpkt("KW18");/*Error writing data*/   /*   decode data to file */
        RESUME;                         /* Send ACK if data written */
    } else ack();                       /* to file OK. */
}

<rdata,rattr>Z {                        /* End Of File (EOF) Packet */
/*  wslots = 1;   DJP fixed comment */  /* Window size back to 1 */
    if (discard) {                      /* If discarding, there's no file */
        discard = 0;                    /* to close, just reset the discard */
        ack();                          /* flag, ack, and proceed normally. */
        success = 1;                    /* Count this as a success. */
        BEGIN rfile;
    } else if (reof(filnam, &iattr) < 0) { /* Close & dispose of the file */
        errpkt("KW19");/*Can't close file*//* If problem, send error message */
        RESUME;                         /* and quit */
    } else {                            /* otherwise */
        ack();                          /* acknowledge the EOF packet */
        success = 1;
        BEGIN rfile;                    /* and await another file */
    }
}

<ssinit>Y {                             /* ACK for Send-Init */
    spar(rdatap);                       /* set parameters from it */
    bctu = bctr;                        /* switch to agreed-upon block check */
    zsetmode(bctu);
    what = W_SEND;                      /* Remember we're sending */
    x = sfile(xflg);                    /* Send X or F header packet */
    if (x) {                            /* If the packet was sent OK */
        resetc();                       /* reset per-transaction counters */
      /*  rtimer();  */                     /* reset timers */
        BEGIN ssfile;                   /* and switch to receive-file state */
    } else {                            /* otherwise send error msg & quit */
      /*  s = xflg ? "Can't execute command" : "Can't open file"; */
        s = xflg ? "KW24" : "KW25";
        errpkt(s);
        RESUME;
    }
}

<ssfile>Y {                             /* ACK for F packet */
    srvptr = srvcmd;                    /* Point to string buffer */
    decode(rdatap,putsrv);              /* Decode data field, if any */
    putsrv('\0');                       /* Terminate with null */
    if (*srvcmd)                        /* If remote name was recorded */
      tlog(F110," stored as",srvcmd,0); /* Record it in transaction log. */
    if (atcapu) {                       /* If attributes are to be used */
        if (sattr(xflg) < 0) {          /* set and send them */
            errpkt("KW20");/*Can't send attributes*/ /* if problem, say so */
            RESUME;                          /* and quit */
        } else BEGIN ssattr;            /* if ok, switch to attribute state */
    } else {
        window(wslotsn);
        if (sdata() < 0) {              /* No attributes, send data */
            clsif(1);                   /* If not ok, close input file, */
            window(wslots = 1);         /* put window size back to 1, */
            seof("");                   /* send EOF packet, */
            BEGIN sseof;                /* and switch to EOF state. */
        } else BEGIN ssdata;            /* All ok, switch to send-data state */
    }
}
<ssattr>Y {                             /* Got ACK to A packet */
    if (rsattr(rdatap) < 0) {           /* Was the file refused? */
        clsif(1);                       /* yes, close it */
        sxeof("D");                     /* send EOF with "discard" code */
        BEGIN sseof;                    /* switch to send-EOF state */
    } else {
        window(wslotsn);                /* Allocate negotiated window. */
        if (sdata() < 0) {              /* File accepted, send data */
            clsif(1);                   /* If problem, close input file */
            window(wslots = 1);         /* Window size back to 1... */
            seof("");                   /* send EOF packet */
            BEGIN sseof;                /* and switch to send-EOF state. */
        } else {                        /* All ok, enter send-data state. */
            BEGIN ssdata;
        }
    }
}
<ssdata>Y {                             /* Got ACK to Data packet */
    canned(rdatap);                     /* Check if file transfer cancelled */
    if ((sdata() < 0) || cxseen || czseen) { /* Try to send next data */
        clsif(0);                       /* If no more data, close file */
        window(wslots = 1);             /* Window size back to 1... */
        if (cxseen || czseen)           /* If interrupted */
          seof("D");                    /* send special EOF packet */
        else seof("");                  /* Otherwise regular EOF packet */
        BEGIN sseof;                    /* And enter send-eof state */
    }
}

<sseof>Y {                              /* Got ACK to EOF */
    success = (cxseen == 0 && czseen == 0); /* Set this for IF command */
    cxseen = 0;                         /* This goes back to zero. */
    if (gnfile() > 0) {                 /* Any more files to send? */
        if (sfile(xflg))                /* Yes, try to send next file header */
          BEGIN ssfile;                 /* if ok, enter send-file state */
        else {                          /* otherwise */
            errpkt("KW21");/*Can't open file*/  /* send error message */
            RESUME;                     /* and quit */
        }
    } else {                            /* No next file */
     /*   tsecs = gtimer(); */              /* get statistics timers */
        seot();                         /* send EOT packet */
        BEGIN sseot;                    /* enter send-eot state */
    }
}

<sseot>Y {                              /* Got ACK to EOT */
    RESUME;                             /* All done, just quit */
}

E {                                     /* Got Error packet, in any state */
    ermsg(rdatap);                      /* Issue message. */
    success = 0;                        /* For IF SUCCESS/FAIL. */
    debug(F101,"ckcpro.w sstate at E pkt","",sstate);
    x = quiet; quiet = 1;               /* Close files silently, */
    clsif(1);
    clsof(1);                           /* discarding any output file. */
  /*  tsecs = gtimer(); */                  /* Get timers */
    quiet = x;                          /* restore quiet state */
/*    if (backgrd && !server) fatal("Protocol error"); DJP commented out */
    xitsta |= what;                     /* Save this for doexit(). */
    RESUME;
}

q { QUIT; }                             /* Ctrl-C interrupt during packets. */

. {                                     /* Anything not accounted for above */
    errpkt("KW22");/*Unexpected packet type*/   /* Give error message */
    xitsta |= what;                     /* Save this for doexit(). */
    RESUME;                             /* and quit */
}
%%




/*  P R O T O  --  Protocol entry function  */

void proto(void)
{
 if(sstate == 'x')
 {                                      /* If entering server mode, */
  server = 1;                           /* set flag, */
  ttoutline(srvtxt);                    /* and issue appropriate message. */
 }
 else
  server=0;

 wart();                               /* Enter the state table switcher. */
    
 if(server)
 {                                     /* Back from packet protocol. */
  server = 0;
  ttoutline(transtoken("KW23"));       /* Hearsay Kermit server finished. */
 }
}

